#include "cdsl.h"

#include <string.h>

#define STR_GROW_RATE(size) \
  ((size+1) * 3 / 2)

#define STR_NEED_SHRINKING(count, size) \
  ((count) < ((size)/2))

struct string {
    int  count, size;
    char *text;
};

void str_clear(string arg)
{
 struct string *str = (struct string*) arg;

    free(str->text);
    str->text = NULL;
    str->count = str->size = 0;
}

void str_new(string *arg, const char *text, int count)
{
 struct string **str = (struct string**) arg;

    *str = (struct string*)malloc(sizeof(struct string));
    (*str)->text = NULL;
    (*str)->size = (*str)->count = 0;

    if (text) {
        str_set(*str, text, count);
    }
}

void str_set(string arg, const char *text, int count)
{
 struct string *str = (struct string*) arg;

    exassert(count >= 0 && "Incorrect count parameter");
    exassert(text != NULL && "Unexpected null pointer");

    str->text = (char*)realloc(str->text, sizeof(char)*(count+1));

    strncpy(str->text, text, count);
    str->text[count] = '\0';
    str->size = str->count = count;
}

void str_cat(string arg, const char *text, int count)
{
 struct string *str = (struct string*) arg;
 int total = 0;

    exassert(count >= 0 && "Incorrect count parameter");
    exassert(str != NULL && "Unexpected null pointer");
    
    total = str->count + count + 1;
    if (total >= str->size) {
        str->text = (char*)realloc(str->text, sizeof(char)*STR_GROW_RATE(total));
        str->size = STR_GROW_RATE(total);
        str->text[str->count] = '\0';
    }
    strncat(str->text, text, count);
    str->count += count;
    str->text[str->count] = '\0';
}

void str_trunk(string arg, int count)
{
 struct string *str = (struct string*) arg;

    exassert(count >= 0 && "Incorrect count parameter");

    if (str->count > count) {
        str->count = count;
        str->text[count] = '\0';
        if (STR_NEED_SHRINKING(str->count, str->size)) {
            str->text = (char *) realloc(str->text, sizeof(char)*STR_GROW_RATE(str->count));
            str->size = STR_GROW_RATE(str->count);
        }
    }
}

void str_free(string *arg)
{
 struct string **str = (struct string**) arg;

    str_clear(*str);
    free(*str);
    *str = NULL;
}

char *str_tochar(const string arg)
{
 struct string *str = (struct string*) arg;

    return ((str) && (str)->count > 0 ? (str)->text : "");
}
  
int str_size(const string arg)
{
 struct string *str = (struct string*) arg;

    return ((str) ? (str)->count : 0);
}
  
int str_empty(const string arg)
{
 struct string *str = (struct string*) arg;

 return ((str) && (str)->count > 0);
}
